/// @file auth_ao.cpp
/// @brief Implementation of the AuthAo class
///
/// This file implements the AuthAo class which handles authentication logic
/// and communicates with the UiAo and DbAo through events.

#include "auth_ao.h"
#include "db_ao.h"
#include "auth_events.h"
#include "bcrypt.h"
#include "spdlog/spdlog.h"
#include <chrono>
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <climits>
#include <cstdint>

namespace
{
Q_DEFINE_THIS_FILE
}

// Global instance of the AuthAo
AuthAo* const the_AO_Auth = new AuthAo();

AuthAo::AuthAo() : QActive(initial), is_authenticated_(false), current_user_id_(-1)
{
    // 不在构造函数中调用 spdlog，避免初始化时的问题
}

Q_STATE_DEF(AuthAo, initial)
{
    Q_UNUSED_PAR(e);

    // Subscribe to UI request events
    subscribe(LOGIN_REQUEST_SIG);
    subscribe(REGISTER_REQUEST_SIG);
    subscribe(LOGOUT_REQUEST_SIG);
    subscribe(SESSION_CHECK_REQUEST_SIG);

    // Subscribe to DB response events
    subscribe(DB_USER_FIND_RESPONSE_SUCCESS_SIG);
    subscribe(DB_USER_FIND_RESPONSE_FAILURE_SIG);
    subscribe(DB_USER_CREATE_RESPONSE_SUCCESS_SIG);
    subscribe(DB_USER_CREATE_RESPONSE_FAILURE_SIG);
    subscribe(DB_SESSION_CREATE_RESPONSE_SUCCESS_SIG);
    subscribe(DB_SESSION_CREATE_RESPONSE_FAILURE_SIG);
    subscribe(DB_SESSION_REMOVE_RESPONSE_SUCCESS_SIG);
    subscribe(DB_SESSION_REMOVE_RESPONSE_FAILURE_SIG);
    subscribe(DB_SESSION_FIND_RESPONSE_SUCCESS_SIG);
    subscribe(DB_SESSION_FIND_RESPONSE_FAILURE_SIG);

    return tran(&processing);
}

Q_STATE_DEF(AuthAo, processing)
{
    QP::QState status_;
    switch (e->sig)
    {
        case LOGIN_REQUEST_SIG:
        {
            const LoginRequestEvt* loginEvt = Q_EVT_CAST(LoginRequestEvt);
            spdlog::info("AUTH AO: Processing login request for user '{}'", loginEvt->username);
            processLoginRequest(loginEvt);
            status_ = tran(&waitingForDbResponse);
            break;
        }
        case REGISTER_REQUEST_SIG:
        {
            const RegisterRequestEvt* registerEvt = Q_EVT_CAST(RegisterRequestEvt);
            spdlog::info("AUTH AO: Processing register request for user '{}'", registerEvt->username);
            processRegisterRequest(registerEvt);
            status_ = tran(&waitingForDbResponse);
            break;
        }
        case LOGOUT_REQUEST_SIG:
        {
            const LogoutRequestEvt* logoutEvt = Q_EVT_CAST(LogoutRequestEvt);
            spdlog::info("AUTH AO: Processing logout request for user ID {}", logoutEvt->user_id);
            processLogoutRequest(logoutEvt);
            status_ = tran(&waitingForDbResponse);
            break;
        }
        case SESSION_CHECK_REQUEST_SIG:
        {
            const SessionCheckRequestEvt* sessionCheckEvt = Q_EVT_CAST(SessionCheckRequestEvt);
            spdlog::info("AUTH AO: Processing session check request for session '{}'", sessionCheckEvt->session_id);
            processSessionCheckRequest(sessionCheckEvt);
            status_ = tran(&waitingForDbResponse);
            break;
        }
        default:
        {
            status_ = super(&top);
            break;
        }
    }
    return status_;
}

Q_STATE_DEF(AuthAo, waitingForDbResponse)
{
    QP::QState status_;
    switch (e->sig)
    {
        case DB_USER_FIND_RESPONSE_SUCCESS_SIG:
        {
            const DbUserFindResponseSuccessEvt* userFindEvt = Q_EVT_CAST(DbUserFindResponseSuccessEvt);
            spdlog::info("AUTH AO: Received user find success response for user ID {}", userFindEvt->user_id);
            processDbUserFindResponseSuccess(userFindEvt);
            status_ = tran(&processing);
            break;
        }
        case DB_USER_FIND_RESPONSE_FAILURE_SIG:
        {
            spdlog::info("AUTH AO: Received user find failure response");
            processDbUserFindResponseFailure(e);
            status_ = tran(&processing);
            break;
        }
        case DB_USER_CREATE_RESPONSE_SUCCESS_SIG:
        {
            const DbUserCreateResponseSuccessEvt* userCreateEvt = Q_EVT_CAST(DbUserCreateResponseSuccessEvt);
            spdlog::info("AUTH AO: Received user create success response for user ID {}", userCreateEvt->user_id);
            processDbUserCreateResponseSuccess(userCreateEvt);
            status_ = tran(&processing);
            break;
        }
        case DB_USER_CREATE_RESPONSE_FAILURE_SIG:
        {
            spdlog::info("AUTH AO: Received user create failure response");
            processDbUserCreateResponseFailure(e);
            status_ = tran(&processing);
            break;
        }
        case DB_SESSION_CREATE_RESPONSE_SUCCESS_SIG:
        {
            const DbSessionCreateResponseSuccessEvt* sessionCreateEvt = Q_EVT_CAST(DbSessionCreateResponseSuccessEvt);
            spdlog::info("AUTH AO: Received session create success response for session ID {}", sessionCreateEvt->session_id);
            processDbSessionCreateResponseSuccess(sessionCreateEvt);
            status_ = tran(&processing);
            break;
        }
        case DB_SESSION_CREATE_RESPONSE_FAILURE_SIG:
        {
            spdlog::info("AUTH AO: Received session create failure response");
            processDbSessionCreateResponseFailure(e);
            status_ = tran(&processing);
            break;
        }
        case DB_SESSION_REMOVE_RESPONSE_SUCCESS_SIG:
        {
            spdlog::info("AUTH AO: Received session remove success response");
            processDbSessionRemoveResponseSuccess(e);
            status_ = tran(&processing);
            break;
        }
        case DB_SESSION_REMOVE_RESPONSE_FAILURE_SIG:
        {
            spdlog::info("AUTH AO: Received session remove failure response");
            processDbSessionRemoveResponseFailure(e);
            status_ = tran(&processing);
            break;
        }
        case DB_SESSION_FIND_RESPONSE_SUCCESS_SIG:
        {
            const DbSessionFindResponseSuccessEvt* sessionFindEvt = Q_EVT_CAST(DbSessionFindResponseSuccessEvt);
            spdlog::info("AUTH AO: Received session find success response for session ID {}", sessionFindEvt->session_id);
            processDbSessionFindResponseSuccess(sessionFindEvt);
            status_ = tran(&processing);
            break;
        }
        case DB_SESSION_FIND_RESPONSE_FAILURE_SIG:
        {
            spdlog::info("AUTH AO: Received session find failure response");
            processDbSessionFindResponseFailure(e);
            status_ = tran(&processing);
            break;
        }
        default:
        {
            status_ = super(&processing);
            break;
        }
    }
    return status_;
}

void AuthAo::processLoginRequest(const LoginRequestEvt* const evt)
{
    // Send request to DB AO to find user by username
#ifdef QEVT_PAR_INIT
    DbUserFindByUsernameRequestEvt* dbEvt = Q_NEW(DbUserFindByUsernameRequestEvt, DB_USER_FIND_BY_USERNAME_SIG, evt->username);
#else
    DbUserFindByUsernameRequestEvt* dbEvt = Q_NEW(DbUserFindByUsernameRequestEvt, DB_USER_FIND_BY_USERNAME_SIG);
    dbEvt->username                       = evt->username;
#endif
    the_AO_Db->POST(dbEvt, this);
}

void AuthAo::processRegisterRequest(const RegisterRequestEvt* const evt)
{
    // Check for empty username or password
    if (evt->username.empty() || evt->password.empty())
    {
        spdlog::info("AUTH AO: Registration failed - username or password is empty");
        // Post REGISTER_RESPONSE_FAILURE event to UI AO
        #ifdef QEVT_PAR_INIT
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
        #else
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
        #endif
        return;
    }

    // Send request to DB AO to check if user already exists
#ifdef QEVT_PAR_INIT
    DbUserFindByUsernameRequestEvt* dbEvt = Q_NEW(DbUserFindByUsernameRequestEvt, DB_USER_FIND_BY_USERNAME_SIG, evt->username);
#else
    DbUserFindByUsernameRequestEvt* dbEvt = Q_NEW(DbUserFindByUsernameRequestEvt, DB_USER_FIND_BY_USERNAME_SIG);
    dbEvt->username                       = evt->username;
#endif
    the_AO_Db->POST(dbEvt, this);
}

void AuthAo::processLogoutRequest(const LogoutRequestEvt* const evt)
{
    // Send request to DB AO to remove session
#ifdef QEVT_PAR_INIT
    DbSessionRemoveRequestEvt* dbEvt = Q_NEW(DbSessionRemoveRequestEvt, DB_SESSION_REMOVE_SIG, evt->user_id);
#else
    DbSessionRemoveRequestEvt* dbEvt = Q_NEW(DbSessionRemoveRequestEvt, DB_SESSION_REMOVE_SIG);
    dbEvt->user_id                   = evt->user_id;
#endif
    the_AO_Db->POST(dbEvt, this);
}

void AuthAo::processSessionCheckRequest(const SessionCheckRequestEvt* const evt)
{
    // Parse the session_id to extract user_id
    // In a real implementation, this would be more complex
    char* endptr;
    long user_id = std::strtol(evt->session_id.c_str(), &endptr, 10);

    // Check if parsing was successful
    if (*endptr != '\0' || user_id < 0 || user_id > INT_MAX)
    {
        spdlog::error("AUTH AO: Session check failed - invalid session ID: {}", evt->session_id);
        // Post SESSION_STATUS_UNAUTHENTICATED event to UI AO
        #ifdef QEVT_PAR_INIT
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, SESSION_STATUS_UNAUTHENTICATED_SIG), this);
        #else
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, SESSION_STATUS_UNAUTHENTICATED_SIG), this);
        #endif
        return;
    }

    // Send request to DB AO to find session by user ID
#ifdef QEVT_PAR_INIT
    DbSessionFindByUserIdRequestEvt* dbEvt = Q_NEW(DbSessionFindByUserIdRequestEvt, DB_SESSION_FIND_BY_USER_ID_SIG, static_cast<int>(user_id));
#else
    DbSessionFindByUserIdRequestEvt* dbEvt = Q_NEW(DbSessionFindByUserIdRequestEvt, DB_SESSION_FIND_BY_USER_ID_SIG);
    dbEvt->user_id                         = static_cast<int>(user_id);
#endif
    the_AO_Db->POST(dbEvt, this);
}

void AuthAo::processDbUserFindResponseSuccess(const DbUserFindResponseSuccessEvt* const evt)
{
    // Check if we're processing a login or registration request
    // This is a simplified approach - in a real implementation, we might use a state variable
    // or include a request ID in the events to track which request this response is for

    // For now, we'll assume this is a login request if we're not authenticated
    if (!is_authenticated_)
    {
        // Verify password
        if (verifyPassword(static_cast<const LoginRequestEvt*>(nullptr)->password, evt->password))
        {
            spdlog::info("AUTH AO: Login successful for user ID {}", evt->user_id);

            // Create session
            auto now        = std::chrono::system_clock::now();
            auto now_time_t = std::chrono::system_clock::to_time_t(now);
            std::stringstream ss;
            ss << std::put_time(std::localtime(&now_time_t), "%Y-%m-%d %H:%M:%S");
            std::string timestamp = ss.str();

            #ifdef QEVT_PAR_INIT
            DbSessionCreateRequestEvt* sessionEvt = Q_NEW(DbSessionCreateRequestEvt, DB_SESSION_CREATE_SIG,
                                                          evt->user_id, generateSessionToken(evt->user_id), timestamp, timestamp);
#else
            DbSessionCreateRequestEvt* sessionEvt = Q_NEW(DbSessionCreateRequestEvt, DB_SESSION_CREATE_SIG);
            sessionEvt->user_id                   = evt->user_id;
            sessionEvt->token                     = generateSessionToken(evt->user_id);
            sessionEvt->created_at                = timestamp;
            sessionEvt->expires_at                = timestamp;  // In a real implementation, this would be in the future
#endif
            the_AO_Db->POST(sessionEvt, this);
        }
        else
        {
            spdlog::info("AUTH AO: Login failed - invalid password");
            // Post LOGIN_RESPONSE_FAILURE event to UI AO
            #ifdef QEVT_PAR_INIT
                    QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_FAILURE_SIG), this);
            #else
                    QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_FAILURE_SIG), this);
            #endif
        }
    }
    else
    {
        // This is a registration request - user already exists
        spdlog::info("AUTH AO: Registration failed - username already exists");
        // Post REGISTER_RESPONSE_FAILURE event to UI AO
        #ifdef QEVT_PAR_INIT
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
        #else
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
        #endif
    }
}

void AuthAo::processDbUserFindResponseFailure(const QP::QEvt* const /*evt*/)
{
    // Check if we're processing a login or registration request
    // For now, we'll assume this is a login request if we're not authenticated
    if (!is_authenticated_)
    {
        spdlog::info("AUTH AO: Login failed - user not found");
        // Post LOGIN_RESPONSE_FAILURE event to UI AO
        #ifdef QEVT_PAR_INIT
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_FAILURE_SIG), this);
        #else
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_FAILURE_SIG), this);
        #endif
    }
    else
    {
        // This is a registration request - user doesn't exist, so we can create it
        // We need to get the username and password from the original request
        // This is a limitation of our simplified approach
        // In a real implementation, we would store the request data or use request IDs
        spdlog::info("AUTH AO: User not found for registration - proceeding with user creation");
        // Post REGISTER_RESPONSE_FAILURE event to UI AO as we don't have the request data
        #ifdef QEVT_PAR_INIT
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
        #else
                QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
        #endif
    }
}

void AuthAo::processDbUserCreateResponseSuccess(const DbUserCreateResponseSuccessEvt* const evt)
{
    spdlog::info("AUTH AO: Registration successful for user ID {}", evt->user_id);
    // Post REGISTER_RESPONSE_SUCCESS event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_SUCCESS_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_SUCCESS_SIG), this);
    #endif
}

void AuthAo::processDbUserCreateResponseFailure(const QP::QEvt* const /*evt*/)
{
    spdlog::info("AUTH AO: Registration failed - could not create user");
    // Post REGISTER_RESPONSE_FAILURE event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, REGISTER_RESPONSE_FAILURE_SIG), this);
    #endif
}

void AuthAo::processDbSessionCreateResponseSuccess(const DbSessionCreateResponseSuccessEvt* const evt)
{
    // Update authentication state
    is_authenticated_      = true;
    current_user_id_       = evt->user_id;
    current_session_token_ = evt->token;

    spdlog::info("AUTH AO: Session created successfully for user ID {}", evt->user_id);
    // Post LOGIN_RESPONSE_SUCCESS event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_SUCCESS_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_SUCCESS_SIG), this);
    #endif
}

void AuthAo::processDbSessionCreateResponseFailure(const QP::QEvt* const /*evt*/)
{
    spdlog::info("AUTH AO: Login failed - could not create session");
    // Post LOGIN_RESPONSE_FAILURE event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_FAILURE_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGIN_RESPONSE_FAILURE_SIG), this);
    #endif
}

void AuthAo::processDbSessionRemoveResponseSuccess(const QP::QEvt* const /*evt*/)
{
    // Update authentication state
    is_authenticated_ = false;
    current_user_id_  = -1;
    current_session_token_.clear();

    spdlog::info("AUTH AO: Logout successful");
    // Post LOGOUT_RESPONSE_SUCCESS event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGOUT_RESPONSE_SUCCESS_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGOUT_RESPONSE_SUCCESS_SIG), this);
    #endif
}

void AuthAo::processDbSessionRemoveResponseFailure(const QP::QEvt* const /*evt*/)
{
    spdlog::info("AUTH AO: Logout failed - could not remove session");
    // Post LOGOUT_RESPONSE_FAILURE event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGOUT_RESPONSE_FAILURE_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, LOGOUT_RESPONSE_FAILURE_SIG), this);
    #endif
}

void AuthAo::processDbSessionFindResponseSuccess(const DbSessionFindResponseSuccessEvt* const evt)
{
    spdlog::info("AUTH AO: Session authenticated for user ID {}", evt->user_id);
    // Post SESSION_STATUS_AUTHENTICATED event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, SESSION_STATUS_AUTHENTICATED_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, SESSION_STATUS_AUTHENTICATED_SIG), this);
    #endif
}

void AuthAo::processDbSessionFindResponseFailure(const QP::QEvt* const /*evt*/)
{
    spdlog::info("AUTH AO: Session unauthenticated");
    // Post SESSION_STATUS_UNAUTHENTICATED event to UI AO
    #ifdef QEVT_PAR_INIT
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, SESSION_STATUS_UNAUTHENTICATED_SIG), this);
    #else
            QP::QActive::PUBLISH(Q_NEW(QP::QEvt, SESSION_STATUS_UNAUTHENTICATED_SIG), this);
    #endif
}

bool AuthAo::verifyPassword(const std::string& password, const std::string& hashed_password) const
{
    // In a real implementation, we would use bcrypt or similar
    // For now, we'll use a simple comparison
    return password == hashed_password;
}

std::string AuthAo::hashPassword(const std::string& password) const
{
    // In a real implementation, we would use bcrypt or similar
    // For now, we'll just return the password
    return password;
}

std::string AuthAo::generateSessionToken(int user_id) const
{
    // Generate a simple token
    return "session_token_" + std::to_string(user_id);
}